package searcher;

import util.cache.normal.*;
import algorithm.text.wordsegment.*;
import java.util.*;
import util.CommonFactory;

/**
 * the class to parse aaa MUST bbb NOT ccc type query
 * aaa bbb ccc will be parsed into three List<String> structures for higher level use
 * @author shitgao
 *
 */

public class QueryParser implements INormalCachable {
	private String originalQuery;
	private List<String> forbiddenStrs;
	private List<String> coerciveStrs;
	private List<String> additiveStrs;
	private WordSegmentor ws;
	
	public QueryParser (String query){
		originalQuery=query;
		ws=CommonFactory.getWordSegmentor();
		parseAString();
	}
	
	public QueryParser () {
		forbiddenStrs=null;
		coerciveStrs=null;
		additiveStrs=null;
		originalQuery=null;
		ws=CommonFactory.getWordSegmentor();
	}
	
	public void parseQuery (String aQuery){
		originalQuery=aQuery;
		parseAString();
	}
	
	private void parseAString(){
		
		int forbiddenStart=-1;
		int coerciveStart=-1;
		if (originalQuery.contains("MUST")){
			coerciveStart=originalQuery.indexOf("MUST");
		}
		if (originalQuery.contains("NOT")){
			forbiddenStart=originalQuery.indexOf("NOT");
		}
		if (forbiddenStart<0 && coerciveStart<0){
			forbiddenStrs=null;
			coerciveStrs=null;
			additiveStrs=ws.segmentToWords(originalQuery, 0, originalQuery.length(), WordSegmentor.ALGORITHM_DYNAMIC);
			return;
		}
		if (forbiddenStart>=0 && coerciveStart<0){
			coerciveStrs=null;
			additiveStrs=ws.segmentToWords(originalQuery, 0, forbiddenStart, WordSegmentor.ALGORITHM_DYNAMIC);
			forbiddenStrs=ws.segmentToWords(originalQuery, forbiddenStart+3, originalQuery.length(), WordSegmentor.ALGORITHM_DYNAMIC);
			return;
		}
		if (forbiddenStart<0 && coerciveStart>=0){
			forbiddenStrs=null;
			additiveStrs=ws.segmentToWords(originalQuery, 0, coerciveStart, WordSegmentor.ALGORITHM_DYNAMIC);
			coerciveStrs=ws.segmentToWords(originalQuery, coerciveStart+4, originalQuery.length(), WordSegmentor.ALGORITHM_DYNAMIC);
			return;
		}
		if (forbiddenStart>coerciveStart){
			additiveStrs=ws.segmentToWords(originalQuery, 0, coerciveStart, WordSegmentor.ALGORITHM_DYNAMIC);
			coerciveStrs=ws.segmentToWords(originalQuery, coerciveStart+4, forbiddenStart, WordSegmentor.ALGORITHM_DYNAMIC);
			forbiddenStrs=ws.segmentToWords(originalQuery, forbiddenStart+3, originalQuery.length(), WordSegmentor.ALGORITHM_DYNAMIC);
		}
		else {
			additiveStrs=ws.segmentToWords(originalQuery, 0, forbiddenStart, WordSegmentor.ALGORITHM_DYNAMIC);
			forbiddenStrs=ws.segmentToWords(originalQuery, forbiddenStart+3, coerciveStart, WordSegmentor.ALGORITHM_DYNAMIC);
			coerciveStrs=ws.segmentToWords(originalQuery, coerciveStart+4, originalQuery.length(), WordSegmentor.ALGORITHM_DYNAMIC);
		}
		return;
	}
	
	public String getOriginalQuery(){
		return originalQuery;
	}
 	
	public List<String> getForbiddenStrings(){
		return forbiddenStrs;		
	}
	
	public boolean hasForbiddenStrings(){
		return (!(forbiddenStrs==null));
	}
	
	public List<String> getCoerciveStrings(){
		return coerciveStrs;
	}
	
	public boolean hasCoerciveStrings(){
		return (!(coerciveStrs==null));
	}
	
	public List<String> getAdditiveStrings(){
		return additiveStrs;
	}
	
	public boolean hasAdditiveStrings() {
		return (!(additiveStrs==null));
	}
	
	//the query contains no legal strings or only forbidden strings should not be executive
	public boolean hasQueryStrings() {
		return (hasAdditiveStrings() || hasCoerciveStrings());
	}
	
	//implements the interface, 
	//here, this.hashcode() need not equal that.hashcode()--I am not sure whether this
	//will cause us problem, I implements a getHashCode() method to make it very possilbe that
	//the two will be simultaneous
	//actually, we check the three query parts of the two to judge whether they are the same
	//I don't think we need change much to make the condition above be exactly obeyed
	//of coz, the change will make the code more ugly,,,
	//another problem of this method is that it is slow which may impair the performance of cache
	public boolean isEquivalentTo(Object o){
		if (o==null || !(o instanceof QueryParser))
			return false;
		QueryParser that=(QueryParser) o;
		return (isSameSet(additiveStrs,that.getAdditiveStrings()) &&
				isSameSet(coerciveStrs,that.getCoerciveStrings()) &&
				isSameSet(forbiddenStrs,that.getForbiddenStrings()) &&
				this.hasAdditiveStrings() == that.hasAdditiveStrings() &&
				this.hasCoerciveStrings() == that.hasCoerciveStrings() &&
				this.hasForbiddenStrings() == that.hasForbiddenStrings());
	}
	
	//this compare may fail when A=(aabb) B=(abbb)
	//but this case is rare, just neglect it
	private boolean isSameSet(List<String> setA, List<String> setB){
		if (setA==null)
			return (setB==null);
		if (setB==null)
			return false;
		if (setA.size()!=setB.size())
			return false;
		return (setA.containsAll(setB));
	}
	
	public int getHashCode(){
		int hashcode=0;
		if (additiveStrs!=null)
			for (int i=0;i<additiveStrs.size();i++)
				hashcode+=additiveStrs.get(i).hashCode();
		if (coerciveStrs!=null)
			for (int i=0;i<coerciveStrs.size();i++)
				hashcode+=coerciveStrs.get(i).hashCode()*2;
		if (forbiddenStrs!=null)
			for (int i=0;i<forbiddenStrs.size();i++)
				hashcode+=forbiddenStrs.get(i).hashCode()*3;
		return hashcode;
	}
}
